#ifndef _STRATEGY_HH_
#define _STRATEGY_HH_
#include "State.h"
#include "Order.hh"
#include "Square.hh"


extern CacheMap torus;
extern State state;




struct Strategy
{

  /// Strategy parameters
  double foodw;
  double hillw;
  double explow;

  //const double survival_instinct = 100;
  // const double myhillw = 8;
  // const double attackw = 4;
  // const double hillw = 4;
  // const double explorationw = 0.01;
  // const double basic_instinct = 0.001;

  Map<float> potential;

  /// Class builder
  Strategy(void) :
    foodw(2),
    hillw(8),
    explow(0.001)
  { }

  void init()
  {
    TRACK();
    ASSERT(torus.size);
    potential.init();
  }

  /// set potentials for attraction/repulsion of ants
  void set_potentials(void);

  /// add attraction toward target to potential map
  template<typename _VisitPredicat>
  void attraction(Map<float> & potential, uLocation target, unsigned maxdepth, unsigned mindepth, unsigned pop, double w,
		  _VisitPredicat should_visit = isLocallyWalkable);


  template<typename _FindPredicat, typename _VisitPredicat>
  void find_closest(std::vector<uLocation> & list, uLocation from, unsigned maxdepth, unsigned pop, _FindPredicat keep_it,
		    _VisitPredicat should_visit = isLocallyWalkable);

};


template<typename _VisitPredicat>
void 
Strategy::attraction(Map<float> & potential, uLocation target, unsigned maxdepth, unsigned mindepth, unsigned pop, double w,
		  _VisitPredicat should_visit)
{
  //  TRACK();
  Map<Square> & map(state.map);
  ASSERT(pop);

  // push starting location
  std::queue<uLocation> locQueue;
  visited.reset();
  locQueue.push(target);
  depth_map[target] = 0;
  visited.insert(target);
 
  while(!locQueue.empty())
    {
      uLocation cLoc = locQueue.front();
      locQueue.pop();


      if(depth_map[cLoc]>mindepth)
	potential[cLoc] += w/pow(1.0 + depth_map[cLoc] - mindepth,2.0);
      else
	potential[cLoc] += w;
      if(isFriend(map.at(cLoc))) 
	{
	  pop--;
	  if(pop == 0) break;
	}


      
      for(unsigned d=0; d<Torus::TDIRECTIONS; d++)
	{
	  uLocation nLoc = torus.getLocation(cLoc, d);
	  if(not visited.count(nLoc) and should_visit(map.at(nLoc)) and depth_map[cLoc]<maxdepth)
	    {
	      locQueue.push(nLoc);
	      depth_map[nLoc] = depth_map[cLoc]+1;
	      visited.insert(nLoc);
	    }
	}
    }
}



template<typename _FindPredicat, typename _VisitPredicat>
void 
Strategy::find_closest(std::vector<uLocation> & list, uLocation from, unsigned maxdepth, unsigned pop, _FindPredicat keep_it,
		       _VisitPredicat should_visit)
{
  Map<Square> & map(state.map);
  list.clear();
  ASSERT(pop);

  // push starting location
  std::queue<uLocation> locQueue;
  visited.reset();
  locQueue.push(from);
  depth_map[from] = 0;
  visited.insert(from);
 
  while(!locQueue.empty())
    {
      uLocation cLoc = locQueue.front();
      locQueue.pop();

      if(keep_it(map.at(cLoc)))
	{
	  list.push_back(cLoc);
	  if(list.size()==pop) break;
	}
      
      for(unsigned d=0; d<Torus::TDIRECTIONS; d++)
	{
	  uLocation nLoc = torus.getLocation(cLoc, d);
	  if(not visited.count(nLoc) and should_visit(map.at(nLoc)) and depth_map[cLoc]<maxdepth)
	    {
	      locQueue.push(nLoc);
	      depth_map[nLoc] = depth_map[cLoc]+1;
	      visited.insert(nLoc);
	    }
	}
    }
}






#endif /* _STRATEGY_HH_ */
